﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Data.Json;
using Windows.Foundation;
using Windows.Security.Authentication.Web;
using Caliburn.Micro;
using F2S.LINQ2LinkedIn.Client.Storage;
using F2S.LINQ2LinkedIn.Model;
using F2S.LINQ2LinkedIn.WinRT;
using F2S.oAuth;
using F2S.OAuth;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace F2S.LINQ2LinkedIn.Client.Services
{
    public class LinkedInService : PropertyChangedBase, ILinkedInService
    {
        public OAuthCredentials Credentials { get; set; }

        public LinkedInStatus Status { get; private set; }

        private string _apiKey = "36jsl5ccuzdt";
        private string _apiSecret = "S6ijg523yu4SaWq9";
        private string _userToken = "26e0421c-9631-40f7-a6bc-a7e7300d05b4";
        private string _userSecret = "9affca5d-b6fa-457a-be63-adfd47d4669b";
        private string _redirectUrl = "http://42InLink.42spikes.com";
        private string _authorizeUrl = "https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id={0}&state={1}&redirect_uri={2}";
        private string _accessTokenUrl = "https://www.linkedin.com/uas/oauth2/accessToken?grant_type=authorization_code&code={0}&redirect_uri={1}&client_id={2}&client_secret={3}";
        
        private string _getPersonUrl = "https://api.linkedin.com/v1/people/~";
        private string _getNetworkUpdatesUrl = "https://api.linkedin.com/v1/people/~/network/updates";

        //private string _accessToken = "AQVQFUcV99xH1pkmqLUliDurPbDwsXKYAVAaElugU_ZQ4am__ITWgIrHQeESk0r-EgganStTykiXGKrCdJaJYfBFFnH1PPHxwYD86fu0TQBt1rAfZVLppVdN-rVhTEegPsPVKHXcWsd5wIGLYsBLyh1o9hxroJoqiAfKGGPbs0rAo-uvWqk";

        private bool _loggedIn = false;

        public bool IsLoggedIn
        {
            get { return _loggedIn; }
            set
            {
                if (value != _loggedIn)
                {
                    _loggedIn = value;
                    NotifyOfPropertyChange(() => IsLoggedIn);
                }
            }
        }

        private bool _requiresAuthorization = false;

        public bool RequiresAuthorization
        {
            get { return _requiresAuthorization; }
            set
            {
                if (value != _requiresAuthorization)
                {
                    _requiresAuthorization = value;
                    NotifyOfPropertyChange(() => RequiresAuthorization);
                }
            }
        }

        private IStorageManager _storageManager;

        public LinkedInService(IStorageManager storageMangaer)
        {
            _storageManager = storageMangaer;
            Credentials = new OAuthCredentials(_apiKey, _apiSecret);
        }

        public async Task<InitializationResult> Initialize()
        {
            var data = _storageManager.LoadData(this);
            if (!string.IsNullOrEmpty(data))
            {
                var settings = JsonConvert.DeserializeObject(data) as JObject;
                Credentials.AccessToken = settings["accessToken"].ToString();
            }

//            fireStatusEvent(StatusEventCode.Initializing);

            if (Credentials.EmptyAccessToken)
            {
                RequiresAuthorization = true;
                IsLoggedIn = false;
                return new InitializationResult(true, false);
            }

            // try to login as a user
            var result = await getPerson();
            if (!result.Success)
            {
                IsLoggedIn = false;
                if (result.StatusCode == OauthExecutionStatusCodes.Unauthorized)
                {
                    RequiresAuthorization = true;

                    return new InitializationResult(requiresAuthorization: true);
                }
            }

            RequiresAuthorization = false;
            IsLoggedIn = true;

            return new InitializationResult(isLoggedIn: true);
        }

        public async Task<AuthorizationResult> Authorize()
        {
            var uri = string.Format(_authorizeUrl, Credentials.AppCredentials.ApiKey, Guid.NewGuid(), _redirectUrl);
            var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
                WebAuthenticationOptions.None,
                new Uri(uri),
                new Uri(_redirectUrl));
            if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
            {
                var codeUrl = new Uri(webAuthenticationResult.ResponseData);
                var query = codeUrl.Query;
                var decoder = new WwwFormUrlDecoder(query);
                var code = decoder.GetFirstValueByName("code");

                var oauth = new OAuthProcessor();
                uri = string.Format(_accessTokenUrl, code, _redirectUrl, Credentials.AppCredentials.ApiKey, Credentials.AppCredentials.ApiSecret);
                var response = await oauth.execute(uri);
                if (response.Success)
                {
                    var auth = JsonConvert.DeserializeObject(response.Data) as JObject;

                    Credentials.AccessToken = auth["access_token"].ToString();

                    save();

                    return new AuthorizationResult(success: true);
                }
            }
            else if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
            {
//                OutputToken("HTTP Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseErrorDetail.ToString());
            }
            else
            {
//                OutputToken("Error returned by AuthenticateAsync() : " + WebAuthenticationResult.ResponseStatus.ToString());
            }

            save();

            return new AuthorizationResult(false);
        }

        private void save()
        {
            var jo = new JsonObject();
            jo["accessToken"] = JsonValue.CreateStringValue(Credentials.AccessToken == null ? "" : Credentials.AccessToken);
            _storageManager.SaveData(this, JsonConvert.SerializeObject(jo));
        }

        public string AuthorizeUrl
        {
            get { return _authorizeUrl; }
            set { _authorizeUrl = value; }
        }

        private void fireStatusEvent(StatusEventCode statusEventCode)
        {
            if (this.StatusEvent != null)
            {
                StatusEvent(this, new StatusEvent(statusEventCode));
            }
        }

        async private Task<OAuthExecutionResult<Person>> getPerson()
        {
            var oap = new OAuthProcessor();
            //var result = await oap.execute<Person>(new OAuthExecutionRequest(credentials, _getPersonUrl));
            var result = await oap.execute<Person>(_getPersonUrl + "?" + getAuthParams());

            return result;
        }

        async public Task<OAuthExecutionResult<UpdateResponse>> GetNetworkUpdates(
            Scope scope = Scope.Network,
            NetworkUpdateTypes networkUpdateTypes = NetworkUpdateTypes.Unspecified,
            int count = -1,
            int start = -1,
            int after = -1,
            int before = -1,
            bool showHiddenMembers = false)
        {
            var builder = new QueryStringBuilder(_getNetworkUpdatesUrl);

            var query = new StringBuilder();
            query.Append(_getNetworkUpdatesUrl);

            if (scope == Scope.Self)
            {
                builder.addQueryParameter("scope", "self");
            }

            foreach (var numi in _networkUpdateTypeStringMap)
            {
                if (((int)networkUpdateTypes & (int)numi.Key) != 0)
                {
                    builder.addQueryParameter("type", numi.Value);
                }
            }

            if (count > 0)
            {
                builder.addQueryParameter("count", count);
            }

            if (start != -1)
            {
                builder.addQueryParameter("after", after);
            }

            if (before != -1)
            {
                builder.addQueryParameter("before", before);
            }

            if (showHiddenMembers)
            {
                builder.addQueryParameter("show-hidden-members", true);
            }

            builder.addQueryParameter("oauth2_access_token", Credentials.AccessToken);

            var oap = new OAuthProcessor();
            var result = await oap.execute<UpdateResponse>(builder.Uri.ToString());

            return result;
        }

        private string getAuthParams()
        {
             return "oauth2_access_token=" + Credentials.AccessToken;
        }

        public event EventHandler<StatusEvent> StatusEvent;

        private readonly static Dictionary<NetworkUpdateTypes, string> _networkUpdateTypeStringMap = new Dictionary
            <NetworkUpdateTypes, string>
        {
            { NetworkUpdateTypes.Application, "APPS"},
            { NetworkUpdateTypes.CompanyFollow, "CMPY"},
            { NetworkUpdateTypes.Connection, "CONN"},
            { NetworkUpdateTypes.Jobs, "JOBS"},
            { NetworkUpdateTypes.JoinedAGroup, "JGRP"},
            { NetworkUpdateTypes.ChangedAPicture, "PICT"},
            { NetworkUpdateTypes.ChangedProfile, "PRFU"},
            { NetworkUpdateTypes.ExtendedProfile, "PRFX"},
            { NetworkUpdateTypes.Recommendation, "RECU"},
            { NetworkUpdateTypes.SharedItem, "SHAR"},
            { NetworkUpdateTypes.Viral, "VIRL"},
        };
    }
}
